package sziit.znk.utils.factory;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import sziit.znk.utils.Enum.Type;
import sziit.znk.utils.JavaLangTypeChose;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class BeanFactory {

    private static Map<String, Object> beanContainer = null;
    private static SAXReader saxReader = null;

    static {
        saxReader = new SAXReader();
        beanContainer = new HashMap<>();
    }

    public static Map<String, Object> getBeanContainer() {
        return beanContainer;
    }

    @SuppressWarnings("all")
    public BeanFactory(String fileName) {
        try {
            URL resource = BeanFactory.class.getClassLoader().getResource(fileName);
            Document read = saxReader.read(resource);
            Element rootElement = read.getRootElement();
            Iterator rootIterator = rootElement.elementIterator();
            while (rootIterator.hasNext()) {
                Element beans = (Element) rootIterator.next();

                if (beans.getName().equals("component-scan")) new ScanNewBean(beans.attributeValue("package"));

                if (beans.getName().equals("bean")) {
                    if (beans.attribute("isRef") == null) {
                        Class aClass = Class.forName(beans.attributeValue("class"));
                        String id = beans.attributeValue("id");
                        Constructor constructor = aClass.getConstructor();
                        Object bean = constructor.newInstance();
                        Iterator properties = beans.elementIterator();
                        while (properties.hasNext()) {
                            Element property = (Element) properties.next();
                            String name = property.attributeValue("name");
                            Field field = aClass.getDeclaredField(name);
                            String[] strings = field.toString().split(" ");
                            Object value = property.attributeValue("value");
                            if (strings[0].equals(Type.QUA_PUBLIC.getJavaType())) {
                                Object o1 = JavaLangTypeChose.javaType(field, value, property);
                                field.set(bean, o1);
                            }
                            if (strings[0].equals(Type.QUA_PRIVATE.getJavaType())) {
                                String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
                                Method method = aClass.getMethod(methodName, field.getType());
                                Object o1 = JavaLangTypeChose.javaType(field, value, property);
                                method.invoke(bean, o1);
                            }
                        }
                        beanContainer.put(id, bean);
                    }

                    if (beans.attribute("isRef") != null && beans.attributeValue("isRef").equals("true")) {
                        String id = beans.attributeValue("id");
                        Class aClass = Class.forName(beans.attributeValue("class"));
                        Constructor constructor = aClass.getConstructor();
                        Object bean = constructor.newInstance();
                        Iterator properties = (Iterator) beans.elementIterator();
                        while (properties.hasNext()) {
                            Element property = (Element) properties.next();
                            String name = property.attributeValue("name");
                            Field field = aClass.getDeclaredField(name);
                            if (property.attribute("ref") == null) {
                                String[] strings = field.toString().split(" ");
                                Object value = property.attributeValue("value");
                                if (strings[0].equals(Type.QUA_PUBLIC.getJavaType())) {
                                    Object o1 = JavaLangTypeChose.javaType(field, value, property);
                                    field.set(bean, o1);
                                }
                                if (strings[0].equals(Type.QUA_PRIVATE.getJavaType())) {
                                    String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
                                    Method method = aClass.getMethod(methodName, field.getType());
                                    Object o1 = JavaLangTypeChose.javaType(field, value, property);
                                    method.invoke(bean, o1);
                                }
                            }
                            if (property.attribute("ref") != null) {
                                String refObject = property.attributeValue("ref").substring(1);
                                Object o = beanContainer.get(refObject);
                                String[] strings = field.toString().split(" ");
                                if (strings[0].equals(Type.QUA_PUBLIC.getJavaType())) {
                                    Field declaredField = bean.getClass().getDeclaredField(refObject);
                                    declaredField.set(bean, o);
                                }
                                if (strings[0].equals(Type.QUA_PRIVATE.getJavaType())) {
                                    String name1 = property.attributeValue("name");
                                    String methodName1 = "set" + name1.substring(0, 1).toUpperCase() + name1.substring(1);
                                    Method method = bean.getClass().getMethod(methodName1, field.getType());
                                    method.invoke(bean, o);
                                }
                            }
                        }
                        beanContainer.put(id, bean);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
